home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Mac-Source 1994 July
/
Mac-Source_July_1994.iso
/
C and C++
/
Miscellaneous
/
Headlines Code
/
Headlines Project ƒ
/
SpyooDisplay.c
< prev
next >
Wrap
Text File
|
1992-11-23
|
17KB
|
708 lines
/*
* SpyooDisplay.c
* Version 1.0.1
*
* This is © Copyright 1992 by Jamie R. McCarthy. All rights reserved.
* Please see HeadlinesMain.c for distribution information.
*
* All the code related to displaying a headline on the screen is in
* this file.
*
* Again, I apologize for the non-prettified code. Things could
* certainly be made much nicer by splitting up some of the long
* functions, and maybe making two or three files out of this one.
* Maybe next release...
*
*/
/******************************/
#include "SpyooDisplay.h"
/******************************/
#include <Global.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <QuickDraw.h>
#include <NeoTextBox.h>
#include <ATMInterface.h>
#include "JMassMalloc.h"
#include "Spyoo.h"
/******************************/
#define kMinSnugFactor (8)
/******************************/
class gFontClass;
Boolean usingATM;
CTabHandle possibleColors;
void getMonitorInfo(GMParamBlockPtr params,
Rect *monitorBounds, Boolean *monitorSupportsColor);
void getWrapBox(GMParamBlockPtr params, Rect *monitorBounds, Rect *theWrapBox);
void setTextJustification(GMParamBlockPtr params,
Rect *theMonitorBounds, Rect *theWrapBox,
short *theTextJustification);
void setTextFont(GMParamBlockPtr params, defn *theDefn, short *theFontNum);
void setTextStyle(GMParamBlockPtr params, defn *theDefn, Rect *theWrapBox,
short fontNum, short *theFontStyle);
Boolean setTextSize(GMParamBlockPtr params, defn *theDefn,
Rect *theMonitorBounds, Rect *theWrapBox,
short fontNum, short fontStyle, Boolean scaleBitmapFonts, short *theFontSize);
void setTextColor(GMParamBlockPtr params, Boolean monitorSupportsColor);
void resetForeColor(GMParamBlockPtr params);
short getMinWidth(short *descent);
/******************************/
OSErr setupDisplay(void)
{
/*
* Change the "file" being read (actually a resource) to the
* resource that describes the fonts and styles, then read
* that information into a bastardized "class," changing the
* C strings with the name and "tags" into Pascal strings
* with the possible styles, terminated by a right brace,
* following the last string character.
*
* This is known as a "skanky hack." Close your eyes.
* Things might get ugly.
*/
OSErr theOSErr;
char **saveGInRsrcHndl = gInRsrcHndl;
long saveGInRsrcMark = gInRsrcMark;
long saveGInRsrcLength = gInRsrcLength;
short oldState = HGetState(gInRsrcHndl);
theOSErr = noErr;
HLock(gInRsrcHndl);
possibleColors = NULL;
gInRsrcHndl = NULL;
usingATM = (initVersionATM(1) != 0); // let's try version 1
SetResLoad(FALSE);
possibleColors = (CTabHandle) GetResource('clut', 999);
SetResLoad(TRUE);
if (possibleColors != NULL) {
LoadResource(possibleColors);
if (*possibleColors == NULL) {
theOSErr = memFullErr;
} else {
DetachResource(possibleColors);
}
}
if (theOSErr == noErr) {
SetResLoad(FALSE);
gInRsrcHndl = GetResource('TEXT', 129);
SetResLoad(TRUE);
if (gInRsrcHndl == NULL) {
theOSErr = noFontInfoErr;
} else {
LoadResource(gInRsrcHndl);
if (*gInRsrcHndl == NULL) {
theOSErr = memFullErr;
} else {
gInRsrcLength = GetHandleSize(gInRsrcHndl);
gInRsrcMark = 0;
}
}
}
if (theOSErr == noErr) {
register defn **update;
defn *dp;
short nFontsFound = 0;
readLine();
update = &(gFontClass.list);
do {
OSErr theOSErr;
theOSErr = process(&dp);
if (theOSErr == noErr) {
short theFontNum;
Boolean useThisFont;
char *leftBrace;
useThisFont = TRUE;
CtoPstr(dp->str);
if (dp->str[0] > 0) {
leftBrace = strchr(&dp->str[1], LBRACE);
dp->str[0] = (leftBrace - &dp->str[0]) - 1;
}
GetFNum(dp->str, &theFontNum);
if (theFontNum != 0) {
*update = dp;
gFontClass.weight += dp->cumul;
dp->cumul = gFontClass.weight;
update = &(dp->next);
++nFontsFound;
} else {
/*
* Since I'm using JMassMalloc, free() doesn't actually
* _do_ anything; however, if it did, I'd write:
free(dp);
*/
}
}
} while (theOSErr == noErr
&& nFontsFound < kMaxNFonts
&& (readLine(), gInLine[0] != '%'));
}
if (gInRsrcHndl != NULL) {
ReleaseResource(gInRsrcHndl);
}
if (theOSErr == noFontInfoErr) {
gFontClass.weight = 1;
gFontClass.list = malloc( sizeof(defn) );
if (gFontClass.list == NULL) {
theOSErr = memFullErr;
} else {
gFontClass.list->cumul = 1;
gFontClass.list->str = (char*) "\pGeneva{18bbib}";
gFontClass.list->next = NULL;
}
}
gInRsrcHndl = saveGInRsrcHndl;
gInRsrcMark = saveGInRsrcMark;
gInRsrcLength = saveGInRsrcLength;
HSetState(gInRsrcHndl, oldState);
return theOSErr;
}
OSErr display(GMParamBlockPtr params, RgnHandle blankRgn,
char *s, short deftag, unsigned short maxNTicksToSnuggle,
Boolean mustDrawNow)
/*
* Given the name of a class, terminated by SLASH, display() will
* (1) expand the class descriptor with evaluate(); (2) pick a font,
* font size, font style, and location; (3) write the evaluated
* descriptor to the screen.
*/
{
OSErr theOSErr;
long initialTicks;
theOSErr = noErr;
initialTicks = TickCount();
gHeadline[0] = 0;
theOSErr = evaluate(s, deftag);
if (theOSErr == noErr) {
Boolean doneOK, giveUp;
doneOK = FALSE;
giveUp = FALSE;
while (!doneOK && (!giveUp || mustDrawNow)) {
Rect theMonitorBounds;
Boolean monitorSupportsColor;
Rect theWrapBox;
unsigned short snugFactor;
short nLinesTotal, initialNLinesTotal;
defn theDefn;
short minWidth, descent;
short theTextJustification;
short theFontNum, theFontSize, theFontStyle;
getMonitorInfo(params, &theMonitorBounds, &monitorSupportsColor);
getWrapBox(params, &theMonitorBounds, &theWrapBox);
theTextJustification = teFlushLeft; // during testing
setTextFont(params, &theDefn,
&theFontNum);
setTextStyle(params, &theDefn, &theWrapBox, theFontNum,
&theFontStyle);
doneOK = setTextSize(params, &theDefn,
&theMonitorBounds, &theWrapBox,
theFontNum, theFontStyle, mustDrawNow, &theFontSize);
if (doneOK || mustDrawNow) {
minWidth = getMinWidth(&descent);
if (theWrapBox.right - theWrapBox.left < minWidth) {
theWrapBox.right = theWrapBox.left + minWidth;
if (theWrapBox.right > theMonitorBounds.right) {
theWrapBox.left -= theWrapBox.right - theMonitorBounds.right;
theWrapBox.right = theMonitorBounds.right;
if (theWrapBox.left < theMonitorBounds.left) {
/* Too big a font and too much text. Give up and try
* again with different text parameters.
*/
doneOK = FALSE;
}
}
}
}
if (doneOK || mustDrawNow) {
short endY;
/* Figure out how many lines there are. */
HidePen();
nLinesTotal = NeoTextBox(&gHeadline[1], gHeadline[0], &theWrapBox,
theTextJustification,
0, // line height code
&endY, NULL);
endY += descent;
/* Fit the wrapbox snugly around the text. */
if (endY < theWrapBox.bottom) {
theWrapBox.top += (theWrapBox.bottom - endY) / 2;
} else {
theWrapBox.top -= (endY - theWrapBox.bottom);
if (theWrapBox.top < theMonitorBounds.top) {
doneOK = FALSE;
}
}
if (doneOK) {
/* See if the wrapbox can be made snugger without adding extra lines. */
initialNLinesTotal = nLinesTotal;
snugFactor = (theWrapBox.right - theWrapBox.left) / 3;
while (nLinesTotal == initialNLinesTotal
&& initialTicks + maxNTicksToSnuggle > TickCount()
&& (theWrapBox.right - theWrapBox.left) > minWidth) {
if ((theWrapBox.right - theWrapBox.left) - snugFactor < minWidth) {
snugFactor = (theWrapBox.right - theWrapBox.left) - minWidth;
}
theWrapBox.left += snugFactor / 2; theWrapBox.right -= snugFactor / 2;
nLinesTotal = NeoTextBox(&gHeadline[1], gHeadline[0], &theWrapBox,
theTextJustification,
0, // line height code
NULL, NULL);
if (nLinesTotal != initialNLinesTotal) {
/* Whoops, we went too far. */
theWrapBox.left -= snugFactor / 2; theWrapBox.right += snugFactor / 2;
snugFactor = (snugFactor * 3) / 8;
if (snugFactor < kMinSnugFactor) {
/* OK, it's tight enough. Quit trying. */
} else {
/* Let's keep trying to make it snugger. */
nLinesTotal = initialNLinesTotal;
}
}
}
}
ShowPen();
if (doneOK || mustDrawNow) {
/* Do it! */
setTextColor(params, monitorSupportsColor);
setTextJustification(params,
&theMonitorBounds, &theWrapBox,
&theTextJustification);
nLinesTotal = NeoTextBox(&gHeadline[1], gHeadline[0], &theWrapBox,
theTextJustification,
0, // line height code
NULL, NULL);
resetForeColor(params);
if (mustDrawNow) {
doneOK = TRUE;
}
}
}
if (!doneOK
&& !mustDrawNow
&& TickCount() > initialTicks + maxNTicksToSnuggle) {
giveUp = TRUE;
}
}
if (giveUp && !mustDrawNow) {
theOSErr = retryDisplayErr;
}
}
return theOSErr;
}
OSErr remove(GMParamBlockPtr params, RgnHandle blankRgn)
/*
* remove() "undraws" the current headline.
*/
{
PenPat(params->qdGlobalsCopy->qdBlack);
PenMode(patCopy);
PaintRgn(blankRgn);
return noErr;
}
OSErr shutdownDisplay(GMParamBlockPtr params)
{
if (possibleColors != NULL) {
DisposHandle(possibleColors);
possibleColors = NULL;
}
}
/******************************/
void getMonitorInfo(GMParamBlockPtr params,
Rect *monitorBounds, Boolean *monitorSupportsColor)
{
short wMonitor;
wMonitor = jrLinearShort(&gJR, 0, params->monitors->monitorCount - 1);
*monitorBounds = params->monitors->monitorList[wMonitor].bounds;
*monitorSupportsColor = (params->monitors->monitorList[wMonitor].curDepth > 2);
}
void getWrapBox(GMParamBlockPtr params, Rect *monitorBounds, Rect *theWrapBox)
{
unsigned short monitorWidth, monitorHeight;
short width, height;
short left, top;
monitorWidth = monitorBounds->right - monitorBounds->left;
monitorHeight = monitorBounds->bottom - monitorBounds->top;
width = jrLinearShort(&gJR, monitorWidth/2, (monitorWidth*3)/4);
height = jrLinearShort(&gJR, monitorHeight/3, (monitorHeight*3)/4);
left = jrLinearShort(&gJR, monitorBounds->left, monitorBounds->right - width);
top = jrLinearShort(&gJR, monitorBounds->top, monitorBounds->bottom - height);
SetRect(theWrapBox, left, top, left+width, top+height);
}
void setTextJustification(GMParamBlockPtr params,
Rect *theMonitorBounds, Rect *theWrapBox,
short *theTextJustification)
{
short leftMargin, rightMargin;
short theJust;
leftMargin = (theWrapBox->left - theMonitorBounds->left);
rightMargin = (theMonitorBounds->right - theWrapBox->right);
if (leftMargin == 0) {
*theTextJustification = teFlushLeft;
} else if (rightMargin == 0) {
*theTextJustification = teFlushRight;
} else {
theJust = (leftMargin > rightMargin) ?
(leftMargin / rightMargin)
: -(rightMargin / leftMargin);
theJust += jrLinearShort(&gJR, -2, 2);
if (theJust < -1) {
*theTextJustification = teFlushLeft;
} else if (theJust > 1) {
*theTextJustification = teFlushRight;
} else {
*theTextJustification = teCenter;
}
}
}
void setTextFont(GMParamBlockPtr params, defn *theDefn, short *theFontNum)
{
defn *dp;
short fontNum;
Boolean fontIsOK;
fontIsOK = FALSE;
while (!fontIsOK) {
register short i;
short wFont;
i = jrLinearShort(&gJR, 0, gFontClass.weight-1);
dp = gFontClass.list;
wFont = 0;
while (dp->cumul <= i) {
dp = dp->next;
++wFont;
}
fontIsOK = ( wFont <= kMaxNFonts );
if (!fontIsOK) DebugStr("\pfontNotOK!?!");
}
GetFNum(dp->str, &fontNum);
if (fontNum == 0) DebugStr("\pbad font name");
TextFont( fontNum );
*theDefn = *dp;
*theFontNum = fontNum;
}
void setTextStyle(GMParamBlockPtr params, defn *theDefn, Rect *theWrapBox,
short theFontNum, short *theFontStyle)
{
unsigned char *cp;
short theStyle;
Boolean mustBePlain;
short chance;
cp = (unsigned char *) &theDefn->str[ theDefn->str[0] + 1];
theStyle = normal;
chance = 1;
mustBePlain = FALSE;
while (*cp != RBRACE && !mustBePlain) {
if (jrLinearShort(&gJR, 0, chance) == 0) {
switch (*cp) {
case 'p': theStyle = normal; mustBePlain = TRUE; break;
case 'b': theStyle |= bold; break;
case 'i': theStyle |= italic; break;
case 'o': theStyle |= outline; break;
case 's': theStyle |= shadow; break;
case 'c': theStyle |= condense; break;
case 'e': theStyle |= extend; break;
}
}
++cp;
chance <<= 1;
}
if (mustBePlain) {
TextFace(normal);
} else {
TextFace(theStyle);
}
*theFontStyle = theStyle;
}
Boolean setTextSize(GMParamBlockPtr params, defn *theDefn,
Rect *theMonitorBounds, Rect *theWrapBox,
short theFontNum, short theFontStyle,
Boolean scaleBitmapFonts, short *theFontSize)
{
short size;
short width, height;
short charsPerLineToTryFor;
short nLinesToTryFor;
unsigned char *cp;
short minSize;
short i;
Boolean foundOK;
foundOK = TRUE;
cp = (unsigned char *) &theDefn->str[ theDefn->str[0] + 1 ] + 1;
minSize = 0;
while ( isdigit(*cp) ) {
minSize *= 10;
minSize += (*(cp++) - '0');
}
/*
* All "magic numbers" in the following section are completely arbitrary.
* I messed around until I got something that works OK.
*/
width = theWrapBox->right - theWrapBox->left;
width = Min(width, 400);
height = theWrapBox->bottom - theWrapBox->top;
height = Min(height, 300);
charsPerLineToTryFor = Max(5, Min(30, width/15));
nLinesToTryFor = Max(2, Min(6, height/40));
while (gHeadline[0] < (charsPerLineToTryFor*nLinesToTryFor)/2) {
if (charsPerLineToTryFor == 5 && nLinesToTryFor == 2) break;
charsPerLineToTryFor = Max(5, charsPerLineToTryFor-2);
nLinesToTryFor = Max(2, nLinesToTryFor-1);
}
while (gHeadline[0] > (charsPerLineToTryFor*nLinesToTryFor)*2) {
if (charsPerLineToTryFor == 30 && nLinesToTryFor == 6) break;
charsPerLineToTryFor = Min(30, charsPerLineToTryFor+2);
nLinesToTryFor = Min(6, nLinesToTryFor+1);
}
size = Min( (width*2)/charsPerLineToTryFor, height/nLinesToTryFor );
/*
* Fiddle with it a bit.
*/
size += jrLinearShort(&gJR, -3, 3);
size = (size > 48) ?
( (size+6) / 12 ) * 12
: ( (size+3) / 6 ) * 6;
size = Max(minSize, Min(72, size));
if (!scaleBitmapFonts) {
if (!fontAvailableATM(theFontNum, theFontStyle)) {
foundOK = (RealFont(theFontNum, size));
/*
* Can we get a real font if we go a few sizes bigger?
*/
if (!foundOK) {
for (i = 1; i <= 4; ++i) {
if (size+i <= 72 && RealFont(theFontNum, size+i)) {
size += i;
foundOK = TRUE;
break;
}
}
}
/*
* How about a few sizes smaller?
*/
if (!foundOK) {
for (i = 1; i <= 10; ++i) {
if (size-i >= minSize && RealFont(theFontNum, size-i)) {
size -= i;
foundOK = TRUE;
break;
}
}
}
}
}
TextSize( size );
*theFontSize = size;
return foundOK;
}
void setTextColor(GMParamBlockPtr params, Boolean monitorSupportsColor)
{
if (params->colorQDAvail
&& monitorSupportsColor
&& params->controlValues[kColorControl]
&& possibleColors != NULL) {
short wColor;
wColor = jrLinearShort(&gJR, 0, (**possibleColors).ctSize);
HLock( (Handle) possibleColors );
RGBForeColor( & (**possibleColors).ctTable[wColor].rgb );
HUnlock( (Handle) possibleColors );
TextMode(srcOr);
} else {
TextMode(srcBic);
}
}
void resetForeColor(GMParamBlockPtr params)
{
if (params->colorQDAvail) {
RGBColor myBlack;
myBlack.red = myBlack.green = myBlack.blue = 0;
RGBForeColor(&myBlack);
}
}
static short getWordWidth(unsigned char *wordPtr, short nChars);
static short getWordWidth(unsigned char *wordPtr, short nChars)
{
PenState oldPS;
Point penLoc;
GetPenState(&oldPS);
HidePen();
MoveTo(0, 0);
DrawText(wordPtr, 0, nChars);
GetPen(&penLoc);
ShowPen();
SetPenState(&oldPS);
return penLoc.h;
}
static short getWidestWordWidth(short widMax);
static short getWidestWordWidth(short widMax)
{
short cChar;
short nChars = gHeadline[0];
short cLength = 0, cWidth, widestWidth = 0;
unsigned char *cp = &gHeadline[1];
unsigned char *wordPtr;
wordPtr = NULL;
for (cChar = 0; cChar < nChars; ++cChar) {
if ( isspace(*cp) ) {
if (cLength*widMax >= widestWidth) {
wordPtr = cp - cLength;
cWidth = getWordWidth(wordPtr, cLength);
if (cWidth > widestWidth) {
widestWidth = cWidth;
}
}
cLength = 0;
} else {
++cLength;
}
++cp;
}
if (cLength*widMax >= widestWidth) {
wordPtr = cp - cLength;
cWidth = getWordWidth(wordPtr, cLength);
if (cWidth > widestWidth) {
widestWidth = cWidth;
}
}
return widestWidth;
}
short getMinWidth(short *descent)
{
FontInfo theFI;
unsigned char *theWord;
GetFontInfo(&theFI);
if (descent != NULL) *descent = theFI.descent;
return getWidestWordWidth(theFI.widMax) + 1; // 1 is an arbitrary slop factor
}